package com.hanson.api;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import com.alibaba.fastjson.JSONObject;
import com.hanson.Const;
import com.hanson.api_bean.MiniLoginRequest;
import com.hanson.common.bean.ResultMessage;
import com.hanson.common.bean.WechatRefundApiResult;
import com.hanson.common.bean.WeiXinConfig;
import com.hanson.common.exception.BusinessException;
import com.hanson.common.service.RedisService;
import com.hanson.common.util.BusinessUtil;
import com.hanson.common.util.WeChatUtils;
import com.hanson.common.util.XmlUtil;
import com.hanson.entity.OrderBean;
import com.hanson.entity.UserBean;
import com.hanson.service.MergeUserService;
import com.hanson.service.OrderService;
import com.hanson.service.PayService;
import com.hanson.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.*;

@Slf4j
@Api(tags = "微信接口管理")
@RestController
@RequestMapping("/api/weixin")
public class WeiXinApiController {
    @Autowired
    private WxMpService wxMpService;
    @Autowired
    private WxMaService wxMaService;
    @Autowired
    private UserService userService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private PayService payService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private WeiXinConfig weiXinConfig;
    @Autowired
    private MergeUserService mergeUserService;
    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * https://www.pianshen.com/article/7398395869/
     *
     * @param code
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "微信公众号根据code获取用户信息")
    @RequestMapping(value = "/getMpOpenIdByCode/{code}", method = RequestMethod.GET)
    public ResultMessage getOpenIdByCode(@PathVariable(value = "code") String code) throws Exception {
        WxMpOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);
        WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, null);
        log.info("MP用户信息：{}", wxMpUser);
        UserBean userBean = userService.selectByOpenId(wxMpUser.getUnionId());
        if (null == userBean) {
            //公众号新客户
            userBean = userService.addUser(wxMpUser);
        } else {
            if (StringUtils.isEmpty(userBean.getMpOpenId())) {
                userBean.setMpOpenId(wxMpUser.getOpenId());
                userBean.setUpdateDate(new Date());
                if (StringUtils.isEmpty(userBean.getHeadImg())) {
                    userBean.setHeadImg(wxMpUser.getHeadImgUrl());
                    if (!StringUtils.isEmpty(wxMpUser.getNickname())) {
                        String nickNameSql = "update t_user set nick_name=?,user_name=? where open_id=?";
                        jdbcTemplate.execute("set names utf8mb4");
                        jdbcTemplate.update(nickNameSql, wxMpUser.getNickname(), wxMpUser.getNickname(), userBean.getOpenId());
                    }
                }
                if (null == userBean.getGender()) {
                    userBean.setGender(wxMpUser.getSex());
                    userBean.setCountry(wxMpUser.getCountry());
                    userBean.setProvince(wxMpUser.getProvince());
                    userBean.setCity(wxMpUser.getCity());
                }
                userService.updateUserAll(userBean);
            }
        }
        UserBean oldUserBean = userService.selectByOpenIdOld(wxMpUser.getOpenId());
        if (null != oldUserBean) {
            //公众号老客户
            userBean.setUserType(oldUserBean.getUserType());
            userBean.setUserName(oldUserBean.getUserName());
            userBean.setNickName(oldUserBean.getNickName());
            userBean.setAccountName(oldUserBean.getAccountName());
            userBean.setPhone(oldUserBean.getPhone());
            userBean.setHeadImg(oldUserBean.getHeadImg());
            userBean.setUserType(oldUserBean.getUserType());
            userBean.setPassword(oldUserBean.getPassword());
            userBean.setScore(userBean.getScore() + oldUserBean.getScore());
            userBean.setMoney(userBean.getMoney() + oldUserBean.getMoney());
            userBean.setIsVip(oldUserBean.getIsVip());
            userBean.setIsSpread(oldUserBean.getIsSpread());
            userBean.setUpdateDate(new Date());
            userService.updateUserAll(userBean);
            mergeUserService.mergeUser(wxMpUser.getUnionId(), wxMpUser.getOpenId(), oldUserBean.getId());
        }
        return ResultMessage.success(userBean, null);
    }

    @ApiOperation(value = "微信小程序根据code获取用户信息")
    @RequestMapping(value = "/getMaOpenIdByCode", method = RequestMethod.POST)
    public ResultMessage getMaOpenIdByCode(@RequestBody MiniLoginRequest miniLoginRequest) throws Exception {
        // 获取微信用户session
        WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(miniLoginRequest.getCode());
        if (null == session) {
            throw new BusinessException("login handler error");
        }
        // 解密用户信息
        WxMaUserInfo wxUserInfo = null;
        try {
            wxUserInfo = wxMaService.getUserService().getUserInfo(session.getSessionKey(),
                    miniLoginRequest.getEncryptedData(), miniLoginRequest.getIv());
        } catch (Exception e) {
            log.error("解密用户信息失败", e.getMessage());
            wxUserInfo = new WxMaUserInfo();
        }
        wxUserInfo.setOpenId(session.getOpenid());
        wxUserInfo.setUnionId(session.getUnionid());
        log.info("MA用户信息：{}", wxUserInfo);
        UserBean userBean = userService.selectByOpenId(wxUserInfo.getUnionId());
        if (null == userBean) {
            userBean = userService.addMiniUser(wxUserInfo);
        } else {
            if (StringUtils.isEmpty(userBean.getMaOpenId())) {
                userBean.setMaOpenId(wxUserInfo.getOpenId());
                userService.updateUserAll(userBean);
            }
        }
        return ResultMessage.success(userBean, null);
    }

    @ApiOperation(value = "根据订单ID获取支付结果")
    @RequestMapping(value = "/getPayResult/{orderSeq}", method = RequestMethod.GET)
    public ResultMessage getPayResult(HttpServletRequest request, @PathVariable String orderSeq) {
        List<OrderBean> orderBeanList = orderService.selectByOrderId(orderSeq);
        if (CollectionUtils.isNotEmpty(orderBeanList)) {
            JSONObject jsonObject = new JSONObject();
            String orderTimeStr = null;
            Integer userScore = 0;
            Integer userMoney = 0;
            Integer payMoney = 0;
            int orderStatus = 1;
            for (OrderBean orderBean : orderBeanList) {
                orderTimeStr = BusinessUtil.date2TimeStr(orderBean.getCreateDate(), "yyyy-MM-dd HH:mm:ss");
                userScore = orderBean.getUserScore();
                userMoney = orderBean.getUserMoney();
                payMoney = orderBean.getPayPrice();
                orderStatus = orderBean.getStatus();
                break;
            }
            Integer orderType = Integer.parseInt(orderSeq.substring(orderSeq.length() - 1, orderSeq.length()));
            jsonObject.put("orderStatus", orderStatus > 1 ? 2 : 1);
            jsonObject.put("orderType", orderType);
            jsonObject.put("orderTime", orderTimeStr);
            jsonObject.put("orderSeq", orderSeq);
            jsonObject.put("payMoney", BusinessUtil.getWebAmount(payMoney));
            jsonObject.put("userMoney", BusinessUtil.getWebAmount(userMoney));
            jsonObject.put("userScore", userScore);
            return ResultMessage.success(jsonObject, null);
        } else {
            throw new BusinessException("订单号不正确");
        }
    }

    @ApiOperation(value = "微信下单", notes = "调用统一支付，返回支付请求参数")
    @RequestMapping(value = "/unifiedOrder/{orderSeq}", method = RequestMethod.POST)
    public ResultMessage unifiedOrder(HttpServletRequest request, @PathVariable String orderSeq) {
        JSONObject jsonObject = new JSONObject();
        for (int tryNum = 0; tryNum < 3; tryNum++) {
            try {
                String unionId = BusinessUtil.getHeaderOpenId(request);
                String client = BusinessUtil.getHeaderClient(request);
                UserBean userBean = userService.selectByOpenId(unionId);
                if (null == userBean) {
                    throw new BusinessException("用户openId不正确！");
                }
                String openId = client.equals(Const.CLIENT_MINI) ? userBean.getMaOpenId() : userBean.getMpOpenId();
                String orderType = orderSeq.substring(orderSeq.length() - 1, orderSeq.length());
                jsonObject.put("orderType", Integer.parseInt(orderType));
                jsonObject.put("orderSeq", orderSeq);
                jsonObject.put("orderTime", BusinessUtil.date2TimeStr(new Date(), "yyyy-MM-dd HH:mm:ss"));
                jsonObject.put("totalFee", "0.0");
                Map<String, Object> map = new HashMap<>();
                map.put("openId", openId);//用户标识openId
                map.put("remoteIp", request.getRemoteAddr());//请求Ip地址
                ModelMap modelMap = new ModelMap();

                Map<String, Object> resultMap = payService.unifiedOrder(map, orderSeq, modelMap, client);
                jsonObject.put("totalFee", modelMap.get("totalFee"));
                // 响应报文
                String return_code = String.valueOf(resultMap.get("return_code"));
                String return_msg = String.valueOf(resultMap.get("return_msg"));
                if (return_code.equalsIgnoreCase("FAIL")) {
                    throw new BusinessException(return_msg);
                } else if (return_code.equalsIgnoreCase("SUCCESS")) {
                    // 返回数据
                    String result_code = String.valueOf(resultMap.get("result_code"));
                    String err_code_des = String.valueOf(resultMap.get("err_code_des"));
                    if (result_code.equalsIgnoreCase("FAIL")) {
                        if (err_code_des.startsWith("201")) {
                            //商户订单号重复
                            String newOrderSeq = BusinessUtil.getOrderSeq(Integer.parseInt(orderSeq.substring(orderSeq.length() - 1, orderSeq.length())));
                            int result = orderService.updateOrderId(orderSeq, newOrderSeq);
                            if (result > 0) {
                                orderSeq = newOrderSeq;
                                continue;
                            } else {
                                throw new BusinessException(err_code_des);
                            }
                        } else {
                            throw new BusinessException(err_code_des);
                        }
                    } else if (result_code.equalsIgnoreCase("SUCCESS")) {
                        SortedMap<String, Object> returnMap = new TreeMap<>();
                        returnMap.put("timeStamp", BusinessUtil.date2TimeStr(new Date(), "yyyyMMddHHmmss"));
                        returnMap.put("appId", String.valueOf(resultMap.get("appid")));
                        returnMap.put("nonceStr", String.valueOf(resultMap.get("nonce_str")));
                        returnMap.put("package", "prepay_id=" + String.valueOf(resultMap.get("prepay_id")));
                        returnMap.put("signType", "MD5");
                        returnMap.put("paySign", WeChatUtils.createSign(returnMap, weiXinConfig.getPaySignKey()));

                        jsonObject.put("timeStamp", returnMap.get("timeStamp"));
                        jsonObject.put("appId", returnMap.get("appId"));
                        jsonObject.put("nonceStr", returnMap.get("nonceStr"));
                        jsonObject.put("package", returnMap.get("package"));
                        jsonObject.put("signType", "MD5");
                        jsonObject.put("openId", openId);
                        jsonObject.put("paySign", returnMap.get("paySign"));
                        jsonObject.put("orderSeq", orderSeq);
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new BusinessException(e.getMessage());
            }
        }
        ResultMessage resultMessage = new ResultMessage();
        resultMessage.setMsg("操作成功！");
        resultMessage.setData(jsonObject);
        return resultMessage;
    }

    @ApiOperation(value = "微信支付回调函数")
    @RequestMapping(value = "/notify", method = RequestMethod.POST, produces = "application/json; charset=utf-8")
    public void notify(HttpServletRequest request, HttpServletResponse response) {
        try {
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/html;charset=UTF-8");
            response.setHeader("Access-Control-Allow-Origin", "*");
            InputStream in = request.getInputStream();
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
            out.close();
            in.close();
            //xml数据
            String reponseXml = new String(out.toByteArray(), "utf-8");
            log.info("【notify resp: {}】", reponseXml);
            WechatRefundApiResult result = (WechatRefundApiResult) XmlUtil.xmlStrToBean(reponseXml, WechatRefundApiResult.class);
            String result_code = result.getResult_code();
            if (result_code.equalsIgnoreCase("FAIL")) {
                //订单编号
                String out_trade_no = result.getOut_trade_no();
                log.error("【ERROR:订单 {}支付失败】", out_trade_no);
                response.getWriter().write(setXml("SUCCESS", "OK"));
            } else if (result_code.equalsIgnoreCase("SUCCESS")) {
                //订单编号
                String out_trade_no = result.getOut_trade_no();
                payService.dealNotify(result);
                log.info("订单 {} 支付成功", out_trade_no);
                response.getWriter().write(setXml("SUCCESS", "OK"));
            }
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
    }

    private static String setXml(String return_code, String return_msg) {
        return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
    }
}
